import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Predicate;

public class MakeParam implements Serializable {

    private static final long serialVersionUID = 1L;

    private String compiler;
    
	private Map<String, StringItem> includeDirs = new HashMap<String, StringItem>();
	private Map<String, StringItem> includeFiles = new HashMap<String, StringItem>();
	private Map<String, StringItem> defines = new HashMap<String, StringItem>();
	
	private Map<String, StringItem> dependHeaderFiles = new HashMap<String, StringItem>();
	private List<String> srcFiles = new ArrayList<String>();

	public void setCompiler(String compiler) {
        this.compiler = compiler;
    }
	
	public String getCompiler() {
        return compiler;
    }
	
	public Map<String, StringItem> getIncludeDirs() {
		return includeDirs;
	}

	public void setIncludeDirs(Map<String, StringItem> includeDirs) {
		this.includeDirs = includeDirs;
	}

	public Map<String, StringItem> getIncludeFiles() {
		return includeFiles;
	}

	public void setIncludeFiles(Map<String, StringItem> includeFiles) {
		this.includeFiles = includeFiles;
	}

	public List<String> getSrcFiles() {
		return srcFiles;
	}

	public void setSrcFiles(List<String> srcFiles) {
		this.srcFiles = srcFiles;
	}
	
	public Map<String, StringItem> getDefines() {
		return defines;
	}
	
	public void setDefines(Map<String, StringItem> defines) {
		this.defines = defines;
	}
	
	public Map<String, StringItem> getDependHeaderFiles() {
        return dependHeaderFiles;
    }
	
	public void setDependHeaderFiles(Map<String, StringItem> dependHeaderFiles) {
        this.dependHeaderFiles = dependHeaderFiles;
    }
	
	private void addParam(Map<String, StringItem> param, String value, int refCount) {
		StringItem item = param.get(value);
		if(item == null) {
			param.put(value, new StringItem(value));
		} else {
			item.incRefCount(refCount);
		}
	}
	
	private void addParam(Map<String, StringItem> param, String value) {
	    addParam(param, value, 1);
	}
	
	public void addIncludeDir(String dir) {
		addParam(includeDirs, dir);
	}
	
	public void addIncludeFile(String file) {
		addParam(includeFiles, file);
	}

	public void addSrcFile(String file) {
		srcFiles.add(file);
	}
	
	public void addDefine(String define) {
		addParam(defines, define);
	}
	
	public void addDependHeaderFiles(String file) {
	    addParam(dependHeaderFiles, file);
	}
	
	private void megeParam(Map<String, StringItem> oldParam, Map<String, StringItem> newParam) {
	    for(Entry<String, StringItem> entry : newParam.entrySet()) {
	        StringItem item = entry.getValue();
	        addParam(oldParam, item.getValue(), item.getRefCount());
	    }
	}
	
	public void merge(MakeParam newParam) {
		
		if("gcc".equals(newParam.getCompiler())) {
			System.err.println("ignore script: " + newParam.getSrcFiles());
			return;
		}
		
	    if(getCompiler() == null) {
	        setCompiler(newParam.getCompiler());
	    } else {
	        if(!getCompiler().equals(newParam.getCompiler())) {
	            System.err.println("compiler is different, first is " + getCompiler() + 
	                    ", second is " + newParam.getCompiler());
	            return;
	        }
	    }
	    getSrcFiles().addAll(newParam.getSrcFiles());
	    megeParam(getIncludeDirs(), newParam.getIncludeDirs());
	    megeParam(getIncludeFiles(), newParam.getIncludeFiles());
	    megeParam(getDefines(), newParam.getDefines());
	    megeParam(getDependHeaderFiles(), newParam.getDependHeaderFiles());
	}
	
	public void filter(MakeParamFilter filter) {
	    final MakeParamFilter paramFilter = filter;
	    
	    defines.entrySet().removeIf(new Predicate<Entry<String, StringItem>>() {

            @Override
            public boolean test(Entry<String, StringItem> t) {
                return !paramFilter.acceptDefine(t.getValue());
            }
        });
	    
	    dependHeaderFiles.entrySet().removeIf(new Predicate<Entry<String, StringItem>>() {

            @Override
            public boolean test(Entry<String, StringItem> t) {
                return !paramFilter.acceptHeader(t.getValue());
            }
        });
	    
	}
	
	private static String paramsToString(Map<String, StringItem> paramMap, String title) {
		StringBuffer buf = new StringBuffer();
		
		buf.append(title + System.lineSeparator());
		for(Entry<String, StringItem> entry : paramMap.entrySet()) {
			StringItem item = entry.getValue();
//			if(item.getRefCount() < 100) {
//				continue;
//			}
			buf.append(String.format(Locale.getDefault(), 
					"[%04d] %s" + System.lineSeparator(), item.getRefCount(), item.getValue()));
//			buf.append(item.getValue() + System.lineSeparator());
		}
		
		return buf.toString();
	}
	
	@Override
	public String toString() {
		StringBuffer buf = new StringBuffer();
		buf.append("compiler:" + getCompiler() + System.lineSeparator());
		buf.append(paramsToString(includeDirs, "include dirs:"));
		buf.append(paramsToString(includeFiles, "include files:"));
		buf.append(paramsToString(defines, "defines:"));
//		buf.append(paramsToString(dependHeaderFiles, "depend header files:"));
		buf.append(String.format(Locale.getDefault(), "total count of src files:%d" + System.lineSeparator(), 
				srcFiles.size()));
		
		return buf.toString();
	}
}
